"""
RabbitMQ订单消费者
处理订单队列中的消息，执行订单处理逻辑
"""
import pika
import json
import logging
import sys
import time
from datetime import datetime
from typing import Dict, Any

# 添加项目根目录到Python路径
sys.path.append('.')

from config.settings import settings
from database.base import SessionLocal
from decimal import Decimal
from models.order_models import Order, OrderItem
from models.goods_models import Goods
from tools.order_stock import finish_order, restore_stock
from services.order_audit import log_order_event, log_inventory_snapshot

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)


class OrderProcessor:
    """订单处理器"""
    
    def __init__(self):
        self.db = SessionLocal()
    
    def calculate_total_amount(self, goods_ids: list, quantities: list) -> Decimal:
        """计算订单总金额"""
        total = Decimal("0")
        try:
            for goods_id, quantity in zip(goods_ids, quantities):
                goods = self.db.query(Goods).filter(Goods.id == goods_id).first()
                if goods:
                    price = Decimal(str(goods.price or 0))
                    total += price * Decimal(quantity)
                else:
                    logger.warning(f"商品ID {goods_id} 不存在")
            return total
        except Exception as e:
            logger.error(f"计算订单金额失败: {e}")
            return Decimal("0")
    
    def create_order_in_db(self, order_data: Dict[str, Any]) -> bool:
        """将订单保存到数据库"""
        try:
            order_no = order_data.get('order_no')
            # 计算总金额
            total_amount = self.calculate_total_amount(
                order_data['goods_ids'], 
                order_data['quantities']
            )
            
            # 创建订单记录
            order = Order(
                order_no=order_data['order_no'],
                user_id=order_data['user_id'],
                total_amount=total_amount,
                status=0,
                created_at=datetime.now()
            )
            
            self.db.add(order)
            self.db.flush()  # 获取订单ID
            
            # 创建订单项
            for goods_id, quantity in zip(order_data['goods_ids'], order_data['quantities']):
                goods = self.db.query(Goods).filter(Goods.id == goods_id).first()
                if goods:
                    order_item = OrderItem(
                        order_id=order.id,
                        goods_id=goods_id,
                        price=goods.price,
                        qty=quantity,
                        name=goods.name or '',
                        image_url=goods.image_url
                    )
                    self.db.add(order_item)
            
            self.db.commit()
            logger.info(f"订单 {order_data['order_no']} 已保存到数据库")
            log_order_event(
                order_no,
                "order_persisted",
                status="success",
                payload={
                    "order_id": order.id,
                    "user_id": order.user_id,
                    "total_amount": str(total_amount),
                    "item_count": len(order_data.get('goods_ids', [])),
                }
            )
            return True
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"保存订单到数据库失败: {e}")
            log_order_event(
                order_data.get('order_no'),
                "order_persist_failed",
                status="error",
                message=str(e)
            )
            return False
    
    def process_order(self, order_data: Dict[str, Any]) -> tuple[bool, bool]:
        """处理订单逻辑
        
        Returns:
            (success, requeue)
            - success=True: 处理成功
            - success=False 且 requeue=True: 处理失败，建议重新入队重试
            - success=False 且 requeue=False: 处理失败，直接确认丢弃（不重试）
        """
        try:
            order_no = order_data.get('order_no')
            logger.info(f"开始处理订单: {order_no}")
            log_order_event(
                order_no,
                "processing_started",
                status="pending",
                payload={
                    "user_id": order_data.get('user_id'),
                    "goods_ids": order_data.get('goods_ids', []),
                    "quantities": order_data.get('quantities', []),
                }
            )
            
            # 1. 检查库存
            for goods_id, quantity in zip(order_data['goods_ids'], order_data['quantities']):
                goods = self.db.query(Goods).filter(Goods.id == goods_id).first()
                if not goods:
                    logger.error(f"商品 {goods_id} 不存在")
                    # 商品不存在：无需重试（数据问题）
                    log_order_event(
                        order_no,
                        "validation_failed",
                        status="failed",
                        message=f"商品 {goods_id} 不存在",
                        payload={"goods_id": goods_id}
                    )
                    return (False, False)
                if goods.stock is None or goods.stock < quantity:
                    logger.error(f"商品 {goods_id} 库存不足，需要 {quantity}，库存 {goods.stock}")
                    # 库存不足：默认不重试，避免消息无限循环
                    stock_before = int(goods.stock or 0) if goods.stock is not None else None
                    log_order_event(
                        order_no,
                        "stock_insufficient",
                        status="failed",
                        message=f"商品 {goods_id} 库存不足，需要 {quantity}，库存 {goods.stock}",
                        payload={"goods_id": goods_id, "need": quantity, "stock": goods.stock}
                    )
                    log_inventory_snapshot(
                        goods_id=goods_id,
                        action="reserve_failed",
                        order_no=order_no,
                        stock_before=stock_before,
                        stock_after=stock_before,
                        change=-int(quantity),
                        source="OrderProcessor",
                        metadata={"stage": "validation"}
                    )
                    return (False, False)
            
            # 2. 保存订单到数据库
            if not self.create_order_in_db(order_data):
                # DB 失败：可以重试
                log_order_event(
                    order_no,
                    "processing_paused",
                    status="retry",
                    message="保存订单到数据库失败，等待重试"
                )
                return (False, True)
            
            # 3. 扣减库存
            for goods_id, quantity in zip(order_data['goods_ids'], order_data['quantities']):
                goods = self.db.query(Goods).filter(Goods.id == goods_id).first()
                if goods:
                    stock_before = int(goods.stock or 0)
                    new_stock = stock_before - int(quantity)
                    goods.stock = new_stock
                    log_inventory_snapshot(
                        goods_id=goods_id,
                        action="reserve_commit",
                        order_no=order_no,
                        stock_before=stock_before,
                        stock_after=new_stock,
                        change=-int(quantity),
                        source="OrderProcessor",
                        metadata={"stage": "db_commit"}
                    )
            
            self.db.commit()
            
            # 4. 其他业务逻辑（如发送通知、记录日志等）
            logger.info(f"订单 {order_data['order_no']} 处理完成")
            log_order_event(
                order_no,
                "processing_completed",
                status="success"
            )
            
            return (True, False)
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"处理订单失败: {e}")
            log_order_event(
                order_data.get('order_no'),
                "processing_failed",
                status="error",
                message=str(e)
            )
            # 系统异常：建议重试
            return (False, True)
    
    def __del__(self):
        """清理资源"""
        if hasattr(self, 'db'):
            self.db.close()


class RabbitMQConsumer:
    """RabbitMQ消费者"""
    
    def __init__(self):
        self.host = getattr(settings, 'RABBITMQ_HOST', '47.122.18.138')
        self.port = getattr(settings, 'RABBITMQ_PORT', 5672)
        self.username = getattr(settings, 'RABBITMQ_USERNAME', 'admin')
        self.password = getattr(settings, 'RABBITMQ_PASSWORD', 'admin')
        self.queue_name = 'order_queue'
        self.queue_arguments = {
            "x-max-priority": 10
        }
        self.processor = OrderProcessor()
    
    def connect(self):
        """连接到RabbitMQ"""
        try:
            credentials = pika.PlainCredentials(self.username, self.password)
            parameters = pika.ConnectionParameters(
                host=self.host,
                port=self.port,
                credentials=credentials,
                heartbeat=600,
                blocked_connection_timeout=300
            )
            self.connection = pika.BlockingConnection(parameters)
            self.channel = self.connection.channel()
            
            # 声明队列
            self.channel.queue_declare(
                queue=self.queue_name,
                durable=True,
                arguments=self.queue_arguments
            )
            
            # 设置QoS，一次只处理一个消息
            self.channel.basic_qos(prefetch_count=1)
            
            logger.info(f"已连接到RabbitMQ: {self.host}:{self.port}")
            return True
            
        except Exception as e:
            logger.error(f"连接RabbitMQ失败: {e}")
            return False
    
    def process_message(self, ch, method, properties, body):
        """处理消息回调函数"""
        try:
            # 解析消息
            message = body.decode('utf-8')
            order_data = json.loads(message)
            
            logger.info(f"收到订单消息: {order_data.get('order_no', 'Unknown')}")
            
            # 处理订单
            success, requeue = self.processor.process_order(order_data)
            if success:
                ch.basic_ack(delivery_tag=method.delivery_tag)
                logger.info(f"订单 {order_data.get('order_no')} 处理成功")
                try:
                    finish_order(order_data.get('order_no', ''))
                    log_order_event(
                        order_data.get('order_no'),
                        "order_finished",
                        status="success",
                        message="Redis 预扣备份已清理"
                    )
                except Exception:
                    pass
            else:
                if requeue:
                    ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
                    logger.error(f"订单 {order_data.get('order_no')} 处理失败，消息重新入队")
                    log_order_event(
                        order_data.get('order_no'),
                        "processing_retry_scheduled",
                        status="retry",
                        message="订单处理失败，消息重新入队"
                    )
                else:
                    # 不重试的失败（如库存不足/商品不存在）：直接确认，避免死循环
                    ch.basic_ack(delivery_tag=method.delivery_tag)
                    logger.error(f"订单 {order_data.get('order_no')} 处理失败（不重试），消息已确认丢弃")
                    try:
                        restore_stock(order_data.get('order_no', ''))
                        log_order_event(
                            order_data.get('order_no'),
                            "processing_failed_no_retry",
                            status="failed",
                            message="订单处理失败且不重试，已触发库存回滚"
                        )
                        log_inventory_snapshot(
                            goods_id=None,
                            action="restore_trigger",
                            order_no=order_data.get('order_no'),
                            source="RabbitMQConsumer",
                            metadata={"reason": "processing_failed_no_retry"}
                        )
                    except Exception:
                        pass
                
        except json.JSONDecodeError as e:
            logger.error(f"消息格式错误: {e}")
            # 确认消息（丢弃格式错误的消息）
            ch.basic_ack(delivery_tag=method.delivery_tag)
        except Exception as e:
            logger.error(f"处理消息异常: {e}")
            # 拒绝消息，重新入队
            ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
    
    def start_consuming(self):
        """开始消费消息"""
        if not self.connect():
            return
        
        try:
            # 设置消息处理回调
            self.channel.basic_consume(
                queue=self.queue_name,
                on_message_callback=self.process_message
            )
            
            logger.info("等待订单消息... 按 Ctrl+C 退出")
            self.channel.start_consuming()
            
        except KeyboardInterrupt:
            logger.info("收到退出信号，正在停止消费者...")
            self.channel.stop_consuming()
            self.connection.close()
            logger.info("消费者已停止")
        except Exception as e:
            logger.error(f"消费消息异常: {e}")
            if hasattr(self, 'connection') and not self.connection.is_closed:
                self.connection.close()


def main():
    """主函数"""
    logger.info("启动RabbitMQ订单消费者...")
    
    consumer = RabbitMQConsumer()
    
    # 无限重试机制（连接失败时持续重试，直到成功）
    retry_interval = 10  # 重试间隔（秒）
    
    while True:
        try:
            if consumer.connect():
                # 连接成功，开始消费
                try:
                    # 设置消息处理回调
                    consumer.channel.basic_consume(
                        queue=consumer.queue_name,
                        on_message_callback=consumer.process_message
                    )
                    
                    logger.info("等待订单消息... 按 Ctrl+C 退出")
                    consumer.channel.start_consuming()
                except KeyboardInterrupt:
                    logger.info("收到退出信号，正在停止消费者...")
                    consumer.channel.stop_consuming()
                    consumer.connection.close()
                    logger.info("消费者已停止")
                    break
                except Exception as e:
                    logger.error(f"消费消息异常: {e}")
                    if hasattr(consumer, 'connection') and not consumer.connection.is_closed:
                        consumer.connection.close()
                    # 连接断开，重新连接
                    logger.info(f"{retry_interval}秒后重新连接...")
                    time.sleep(retry_interval)
            else:
                # 连接失败，等待后重试
                logger.warning(f"RabbitMQ连接失败，{retry_interval}秒后重试...")
                time.sleep(retry_interval)
        except KeyboardInterrupt:
            logger.info("收到退出信号，停止消费者...")
            break
        except Exception as e:
            logger.error(f"消费者异常: {e}")
            logger.info(f"{retry_interval}秒后重试...")
            time.sleep(retry_interval)


if __name__ == "__main__":
    main()
